package pub.cleangao.mybatis.runtime;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.extern.slf4j.Slf4j;
import pub.cleangao.common.util.JsonUtil;
import pub.cleangao.mybatis.annotation.Dialect;
import pub.cleangao.mybatis.meta.ColumnDefinition;
import pub.cleangao.mybatis.meta.MapperInterfaceDefinition;
import pub.cleangao.mybatis.meta.TableDefinition;
import pub.cleangao.mybatis.util.ClassUtil;
import pub.cleangao.mybatis.meta.EntityInformation;
import pub.cleangao.mybatis.util.TableNameAs;
import java.lang.reflect.Field;
import java.util.*;


@Slf4j
public class MybatisContext {

    // key: 实体类的Class
    private static final Map<Class<?>, EntityInformation> entityInfoMap = new HashMap<>();
    // 实体映射, key:entityClass value:TableDefinition
    private static final Map<Class<?>, TableDefinition> tableDefinitionMap = new HashMap<>();
    // key:tableName value:TableDefinition
    private static final Map<String, TableDefinition> tableNameDefinitionMap = new HashMap<>();
    // Mapper接口映射
    private static final Map<Class<?>, MapperInterfaceDefinition> mapperDefinition = new HashMap<>();
    // mapper --> entity
    private static final BiMap<Class<?>, Class<?>> mapperEntity = HashBiMap.create();
    // key:Mapper.xml 文件路径 value:entityClass
    private static final BiMap<String, Class<?>> mapperXmlMap = HashBiMap.create();
    private static final Map<String, String> mapperXmlContent = new HashMap<>();
    // 正在使用的tableAs标识
    private static final List<String> inUseTableAs = new ArrayList<>();
    // key:tableName value:tableAs
    private static final BiMap<String, String> tableToTableAs = HashBiMap.create();

    /**
     * PkColumnName
     *
     * @param entityClass 实体类的Class
     * @return 主键列名
     */
    public static String getPkColumnName(Class<?> entityClass) {
        EntityInformation entityInfo = getEntityInfo(entityClass);
        if (entityInfo == null) {
            return null;
        }
        return entityInfo.getPkColumnName();
    }

    /**
     * JavaBean 字段名
     *
     * @param entityClass 实体的Class
     * @return Pk的Java字段名
     */
    public static String getPkJavaFieldName(Class<?> entityClass) {
        EntityInformation entityInfo = getEntityInfo(entityClass);
        if (entityInfo == null) {
            return null;
        }
        return entityInfo.getPkJavaFieldName();
    }

    /**
     * 查询实体类信息
     *
     * @param entityClass 实体类class
     * @return EntityInformation
     */
    public static EntityInformation getEntityInfo(Class<?> entityClass) {
        return entityInfoMap.get(entityClass);
    }

    /**
     * 获取主键值
     *
     * @param entity 实体类
     * @return 主键值
     */
    public static Object getPkValue(Object entity) {
        if (entity == null) {
            return null;
        }
        Class<?> entityClass = entity.getClass();
        String pkJavaName = getPkJavaFieldName(entityClass);
        Field field = ClassUtil.findField(entityClass, pkJavaName);
        if (field == null) {
            return null;
        }
        ClassUtil.makeAccessible(field);
        try {
            return field.get(entity);
        } catch (IllegalAccessException e) {
            log.error("reflect error: {}", e.getMessage());
            return null;
        }
    }


    /**
     * 添加实体-表映射定义类
     *
     * @param clazz           实体类的Class
     * @param tableDefinition 表映射描述
     */
    public static void addTableDefinition(Class<?> clazz, TableDefinition tableDefinition) {
        tableDefinition.setTableAs(TableNameAs.next(inUseTableAs));
        tableDefinitionMap.put(clazz, tableDefinition);
        EntityInformation entityInfo = new EntityInformation();
        ColumnDefinition pkColumn = tableDefinition.getPkColumn();
        if (pkColumn != null) {
            entityInfo.setPkColumnName(pkColumn.getColumnName());
            entityInfo.setPkJavaFieldName(pkColumn.getFieldName());
        }
        entityInfo.setClassName(clazz.getName());
        entityInfo.setSimpleClassName(clazz.getSimpleName());
        entityInfo.setTableName(tableDefinition.getTableName());
        entityInfo.setTableCleavage(tableDefinition.getCleavage());
        entityInfo.setCleavageClass(tableDefinition.getCleavageClass());
        entityInfoMap.put(clazz, entityInfo);
        tableToTableAs.put(tableDefinition.getTableName(), tableDefinition.getTableAs());
        tableNameDefinitionMap.put(tableDefinition.getTableName(), tableDefinition);
        if (Mode.DEBUG) {
            System.out.println("[" + clazz.getName() + "]: \n  " + "table: " + JsonUtil.pojoToJson(tableDefinition) + "\n  " + "entity: " + JsonUtil.pojoToJson(entityInfo));
        }

    }


    public static void addMapperDefinition(Class<?> mapperClass, MapperInterfaceDefinition interfaceDefinition) {
        mapperDefinition.put(mapperClass, interfaceDefinition);
    }

    public static TableDefinition getTableDefinition(Class<?> entityClass) {
        return tableDefinitionMap.get(entityClass);
    }

    public static TableDefinition getMapperTableDefinition(Class<?> mapperClass) {
        Class<?> entityClass = mapperEntity.get(mapperClass);
        return tableDefinitionMap.get(entityClass);
    }

    public static EntityInformation getMapperEntityInformation(Class<?> mapperClass) {
        Class<?> entityClass = mapperEntity.get(mapperClass);
        return getEntityInfo(entityClass);
    }

    /**
     * 记录新生成的xml文件路径
     *
     * @param xmlFile Mapper.xml路径
     */
    public static void addXmlMapper(String xmlFile, Class<?> entityClass) {
        mapperXmlMap.put(xmlFile, entityClass);
    }

    public static Map<String, Dialect> getMapperXmlDialectMap() {
        Map<String, Dialect> rst = new HashMap<>();
        for (Map.Entry<String, Class<?>> entry : mapperXmlMap.entrySet()) {
            String key = entry.getKey();
            Dialect value = tableDefinitionMap.get(entry.getValue()).getDialect();
            rst.put(key, value);
        }
        return rst;
    }


    public static void mapperToEntity(Class<?> mapperClass, Class<?> entityClass) {
        mapperEntity.put(mapperClass, entityClass);
    }

    public static void addMapperXmlContent(String path, String content) {
        mapperXmlContent.put(path, content);
    }

    public static String getMapperXmlContent(String path) {
        return mapperXmlContent.get(path);
    }

    public static String getTableAs(String tableName) {
        return tableToTableAs.get(tableName);
    }


    /**
     * Mapper使用的实体
     *
     * @param mapperClass mapper interface class
     */
    public static Class<?> getMapperEntityClass(Class<?> mapperClass) {
        return mapperEntity.get(mapperClass);
    }
}
